﻿// @include "Namespace.js"
// @include "Typs.js"
// @include "Extend.js"
// @include "Function.js"
// @include "Argument.js"
// @include "Object.js"
/**
 * @fileOverview 模块引入工具
 * 
 * @version qinglan_Module 1.0
 * @static
 * @author wxq wxqqh@qq.com
 */
/**
 * 命名空间
 * 
 * @namespace
 * @type Object
 */
window.qinglan = window.qinglan || {};
/**
 * 模块引入工具命名空间
 * 
 * @namespace
 * @type Object
 */
qinglan.Module = qinglan.Module || {};

/**
 * 存放模块信息
 * 
 * @type Object
 */
qinglan.Module.cache = {
	ReqModules : {},
	DefaultDelayTime : 10,
	LoadingScripts : {},
	LoadingModules : {},
	LoadedScripts : {},
	LoadedModules : {}
};
/**
 * 创建模块
 * @param {String} name 模块名字
 * @param {String}{Array} urls 模块自身js
 * @param {Object} opt 设置参数
 * @return {qinglan.Module.createModule} 模块
 */
qinglan.Module.createModule = function(name,urls,opt){
	this.name = name;
	/**
	 * 本模块引用js
	 * @field
	 */
	this.urls = [];
	/**
	 * 依赖模块
	 * @field
	 */
	this.requirements = [];
	/**
	 * 加载参数设置
	 * @field
	 */
	this.opt = qinglan.Extend.simple({isAsyn:true,delay:qinglan.Module.cache.DefaultDelayTime},opt,1);

	/**
	 * 注册依赖模块
	 * @param {String}{Array} 依赖模块
	 * @function
	 */
	this.require = function(moduleLIst){
		var args = qinglan.Argument.toArray(arguments);
		qinglan.Object.each(args,function(param){
			qinglan.Type.isArray(param) ? qinglan.Object.each(param,function(p){
				this.requirements.push({'name' : p});
			},this) : this.requirements.push({'name' : param});
		},this);
	};

	qinglan.Type.isArray(urls) ? this.urls = urls : (urls && this.urls.push(urls));
	
	return this;
};

/**
 * 注册模块
 * @param {String} name 模块名字
 * @param {String}{Array} urls 模块自身js
 * @param {Object} opt 设置参数
 * @param {Boolean} rewrite 进行模块重写
 * @return {qinglan.Module.createModule} 模块对象
 */
qinglan.Module.register = function(name,urls,opt,rewrite){
	var ReqModules = qinglan.Module.cache.ReqModules;
	var module = new qinglan.Module.createModule(name,urls,opt);
	if(ReqModules[module.name]){
		rewrite ? (ReqModules[module.name] = module && qinglan.Debug.info('Module : ['+module.name+'] will be Overwritten!')) : qinglan.Debug.info('Module : ['+module.name+']  was already registered!');
	}else{
		ReqModules[module.name] = module;
		qinglan.Debug.info('Module : ['+module.name+']  was registered!');
	}
	return module;
};
/**
 * 加载模块
 * 
 * @param {qinglan.Module.createModule} module 模块对象
 * @param {Function} callback 回调函数
 */
qinglan.Module.loadModule = function(module,callback){
	var Module = qinglan.Module;
	var loadScript = Module.cache.LoadedScripts;
	var loadingScript = Module.cache.LoadingScripts;
	var totalLoadLen = module.urls.length;
	var finishCount = 0;
	
	var checkScriptLoad = function(){
		var falg = true;
		qinglan.Object.each(module.urls,function(url){
			if(!loadScript[url]){falg = false;}
		});
		return falg;
	};

	var defaultCallback = function(url,flag){
		qinglan.Debug.info('Script : ['+url+'] was loaded ' + (flag ? 'success' : 'failed'))+'!';
		loadScript[url] = flag;
		delete loadingScript[url];
		finishCount++;
		if(finishCount == totalLoadLen && callback) {
			 callback(module.name,checkScriptLoad());
		}
		return false;
	};
 
	totalLoadLen == 0 ? callback(module.name,true): qinglan.Object.each(module.urls,function(url){
		if(!loadScript[url] && !loadingScript[url]){
			loadingScript[url] = true;
			module.opt.isAsyn ? Module._asynLoadScript(module.name,url,module.opt.delay,defaultCallback) :  Module._xhrLoadScrip(module.name,url,defaultCallback) ;
		}else{
			qinglan.Debug.info('Script : ['+url+'] was already ' + (loadingScript[url] ? 'loading' : 'loaded'))+'!';
		}
	});
};
/**
 * 使用xhr加载js
 * @param {String} moduleName 模块名字
 * @param {String} url 加载js的路径 
 * @param {Function} callback 回调队列
 */
qinglan.Module._xhrLoadScript = function(moduleName,url,callback){
	var xhr;
    if (window.XMLHttpRequest)
		xhr = new XMLHttpRequest();
	else if (window.ActiveXObject) {
		xhr = new ActiveXObject("Microsoft.XMLHTTP"); 
	}
	xhr.onreadystatechange = function(){
		if(xhr.readyState == 4){
			if( xhr.status == 200){
				qinglan.Module._injectScript(xhr.responseText,moduleName);
				qinglan.Module.cache.LoadedScripts[url] = true;
				callback && callback(url,true);
			}else if(xhr.status >= 400){
				callback && callback(url,false);
			}
		}
	};
    xhr.open("GET", url, true);
    xhr.send(null);
};
/**
 * 使用scriptTag加载js
 * @param {String} moduleName 模块名字
 * @param {String} url 加载js的路径
 * @param {Number} delay 延迟的时间
 * @param {Function} callback 回调队列
 */
qinglan.Module._asynLoadScript = function(moduleName,url,delay,callback){
	var scriptNode = qinglan.Module._createScriptNode(moduleName);
	scriptNode.onload= scriptNode.onreadystatechange = function(){
		if ((!scriptNode.readyState) || scriptNode.readyState == "loaded" || scriptNode.readyState == "complete" || scriptNode.readyState == 4 && scriptNode.status == 200) {
			callback && callback(url,true);	     	
	    }
	    return false;
	};
	scriptNode.onerror = function(){
		callback && callback(url,false);
		return false;
	};
	scriptNode.setAttribute('src',url);
	document.getElementsByTagName("head")[0].appendChild(scriptNode);
};

/**
 * 创建script标签
 * @param {String} scriptName 节点名字
 * @return {Element} script节点
 */
qinglan.Module._createScriptNode = function(scriptName) {
    var scriptNode = document.createElement("script");
    scriptNode.setAttribute("type", "text/javascript");
    scriptNode.setAttribute("language", "Javascript");
    try {
        scriptNode.setAttribute("name", scriptName);
    } catch (err) { }
    return scriptNode;    
};
/**
 * 将请求得到js插入到dom中
 * @param {String} scriptText 
 * @param {String} scriptName
 */
qinglan.Module._injectScript = function (scriptText, scriptName) {
    var scriptNode = qinglan.Module._createScriptNode(scriptName);
    scriptNode.text = scriptText;
    document.getElementsByTagName("head")[0].appendChild(scriptNode);
};
/**
 * 模块主函数
 * @param {String}{Array} moduleName 要使用的模块的名字
 * @param {Function} callback 加载模块之后的回调函数
 * @param {Object} context 回调绑定对象
 * @return {}
 * 
 * @function
 * @private
 * 
 */
qinglan.Module.use= function(moduleName, callback, context){
	var Module = qinglan.Module;
	
	if(qinglan.Type.isArray(moduleName)){
		var mutiModule = moduleName.join('_');
		Module.register(mutiModule,'').require(moduleName);
		Module.use(mutiModule,function(flag){
			callback.call(context,flag);
		});
		return true;
	}
	
	var module = Module.cache.ReqModules[moduleName];
	var loadModule = Module.cache.LoadedModules;
	var loadingModule = Module.cache.LoadingModules;
	var totalLen = module.requirements.length;
	var finishCount = 0;
	
	if(!module){
		qinglan.Debug.error('Module : ['+moduleName+'] was not registered!!');
		callback && callback(moduleName,false);
		return false;
	}
	if(loadModule[moduleName] || loadingModule[moduleName]){//如果已经加载了就不用再加载咯
		qinglan.Debug.info('Module : ['+moduleName+'] was already'+(loadingModule[moduleName] ? ' loading ' : ' loaded ')+ ' !!');
		callback && callback(moduleName,true);
		return true;		
	}
	
	var checkModuel = function(){
		var flag = true;
		qinglan.Object.each(module.requirements,function(reqModule){
			if(!Module.cache.LoadedModules[reqModule.name]){flag = false;}	
		});
		return flag;
	};
	
	var doLoadModule = function (cb,context){
		loadingModule[moduleName] = true;
		Module.loadModule(module,function(name,flag){
				qinglan.Debug.info('Module : ['+name+'] was loaded ' + (flag ? 'success' : 'failed'))+'!';
				loadModule[moduleName] = flag;
				delete loadingModule[moduleName];
				cb && cb.call(context,checkModuel());
			});
	};
	
	var defaultCallback = function(reqModuleName,flag){
		finishCount++;
		if(finishCount == totalLen){
			doLoadModule(callback,context);
		}
		return false;
	};
	
	totalLen == 0 ? doLoadModule(callback,context) : qinglan.Object.each(module.requirements,function(reqModule){
		qinglan.Module.use(reqModule.name,defaultCallback,context);
	});
	return true;
};

